﻿using SCG.General;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Resources;
using System.Text;
using System.Threading.Tasks;
using TTEngine.Properties;

namespace TTEngine
{
    public abstract class MapObject : TaggedObject
    {
        //constructor
        public MapObject()
        {
            Quantity = 1;
            UnitWeight = 1;
            LootLevel = -1;
            CanCollapse = true;
            WeaponSpeed = 6;
            AttackName = "attacks";
        }

        //display name
        public virtual string DisplayName
        {
            get
            {
                return ObjectName;
            }
        }
        public virtual string PluralDisplayName
        {
            get
            {
                if (DisplayName.EndsWith("ch"))
                    return DisplayName + "es";
                else if (DisplayName.EndsWith("sh"))
                    return DisplayName + "es";
                else
                    return DisplayName + "s";
            }
        }
        public virtual string DisplayLabel
        {
            get
            {
                if (Quantity == 1)
                    return DisplayName;
                else
                    return Quantity + " " + PluralDisplayName;
            }
        }

        //cloning
        public abstract MapObject Clone();

        //image
        public virtual Bitmap Image
        {
            get
            {
                ResourceManager rm = Resources.ResourceManager;
                Bitmap bmp = (Bitmap)rm.GetObject(ObjectName);
                return bmp;
            }
        }
        public bool ImagesAreUnique { get; set; }

        //if positive, will decrement and then remove upon completion
        public int Lifetime { get; set; }

        //quantity
        public int Quantity { get; set; }

        //collapse?
        public bool CanCollapse { get; set; }

        //weight
        public float UnitWeight { get; set; }

        public float Weight
        {
            get
            {
                return UnitWeight * Quantity;
            }
        }

        //weapon?
        public bool IsWeapon { get; set; }

        //damage when wielded
        public DamageExpressionList Damage { get; set; }

        //weapon speed
        public int WeaponSpeed { get; set; }

        //attack name
        public virtual string AttackName { get; set; }

        //loot?
        public int LootLevel { get; set; }

        //dungeon dressing?
        public int DressingLevel { get; set; }

        //public properties
        public object Sprite { get; set; }

        //illumination map
        public virtual ByteMatrix IllumMap
        {
            get
            {
                return null;
            }
        }

        //adjacency
        public bool AdjacentTo(int x, int y)
        {
            return Math.Abs(X - x) <= 1 && Math.Abs(Y - y) <= 1;
        }
        public bool AdjacentTo(SCGPoint pt)
        {
            return AdjacentTo(pt.X, pt.Y);
        }

        //gear?
        public bool CanTake { get; set; }
        public bool CanReady { get; set; }
        public int HandsRequired { get; set; }

        //public properties
        public DungeonLevel Level { get; set; }
        public int X { get; set; }
        public int Y { get; set; }
        public bool BlocksMovement { get; set; }

        //If Flamability > 0, can be burned.  Value indicates chance that an adjacent fire can ignite it.
        public int Flamability { get; set; }

        //How long does it burn once ignited?
        public int BurnLifetime { get; set; }

        //fragility
        public virtual bool BreaksWhenStressed(StressType st)
        {
            return false;
        }

        public SCGPoint Position
        {
            get
            {
                return new SCGPoint(X, Y);
            }
        }

        //actor?
        public virtual bool IsActor
        {
            get
            {
                return false;
            }
        }

        //whose the owner
        public Actor Owner { get; set; }

        //callback for object just added to map
        //called from DungeonLevel.AddObjectToMap
        public virtual void AddedToMap()
        {
        }

        //readied/unreadies
        public virtual void Readied()
        {
            if (Owner != null && Owner.IsPC && Owner.Level != null)
            {
                string s = Owner.DisplayName + " readies the " + DisplayName;
                if (HandsRequired == 1)
                    s += " in their hand.";
                else if (HandsRequired == 2)
                    s += " in their hands.";
                else
                    s += ".";
                Owner.Level.Host.DisplayMessage(s);
            }
        }
        public virtual void UnReadied()
        {
        }

        //breaks
        public virtual void Breaks(DungeonLevel level, int x, int y)
        {
            level.Host.DisplayMessage("The " + DisplayName + " breaks!");
        }

        //does it prohibit someone from moving out of it?
        public virtual bool ProhibitsMovement(Actor actor)
        {
            return false;
        }

        //consume one of the items
        public void ConsumeOne()
        {
            Quantity--;
            if (Quantity <= 0)
            {
                if (Owner != null)
                {
                    Actor actor = Owner;
                    Owner.RemoveGear(this);
                    actor.Level.Host.ActorUpdated(actor);
                }
                else
                    Level.RemoveObjectFromMap(this);
            }
        }

        //what happens when you burn it?
        public virtual void Burn()
        {
            if (Flamability > 0)
            {
                string s = "The " + DisplayLabel + " ";
                if (BurnLifetime > 0)
                {
                    if (Quantity == 1)
                        s += "catches";
                    else
                        s += "catch";
                    s += " fire!";
                    Lifetime = BurnLifetime;
                    Fire fire = new Fire();
                    fire.Lifetime = BurnLifetime;
                    Level.AddObjectToMap(fire, X, Y);
                }
                else
                {
                    if (Quantity == 1)
                        s += "burns";
                    else
                        s += "burn";
                    s += " away!";
                }
                Level.Host.DisplayMessage(s, X, Y);
                Level.RemoveObjectFromMap(this);
            }
        }

        //do any processing required if this is a loot item
        public virtual void LootProcessing(int difficultyLevel)
        {
        }

        //is it "readied"?
        public bool IsReadied
        {
            get
            {
                if (Owner == null)
                    return false;
                if (Owner.ReadiedGear == null)
                    return false;
                foreach (MapObject mo in Owner.ReadiedGear)
                    if (mo == this)
                        return true;
                return false;
            }
        }

        //impulse processing
        public virtual void ProcessImpulse()
        {
            if (Lifetime > 0)
            {
                Lifetime--;
                if (Lifetime == 0)
                    if (Level != null)
                        Level.RemoveObjectFromMap(this);
            }
        }

        //action infrastructure
        //actions visible when an adjacent actor clicks on the object
        public virtual List<string> GetActionsAdjacent(Actor actor)
        {
            List<string> lst = _actions;
            lst.Clear();

            //can take it?
            if (CanTake)
                lst.Add("Take");

            //can burn it?
            if (Flamability > 0)
                if (actor.HasReadiedGearWithTag("FireSource"))
                    lst.Add("Burn");

            return lst;
        }
        public virtual void ProcessAdjacentAction(Actor actor, string action, int qty)
        {
            if (!actor.AdjacentTo(X, Y))
                return;

            switch (action)
            {
                case "Burn":
                    {
                        actor.Level.Host.DisplayMessage(actor.DisplayName + " burns the " + DisplayLabel + ".");
                        Burn();
                        break;
                    }
                case "Take":
                    {
                        //if only taking part of the gear, leave the rest behind
                        MapObject amtTaken = this;
                        if (qty != Quantity)
                        {
                            amtTaken = Clone();
                            amtTaken.Quantity = qty;
                            Quantity -= qty;
                            actor.AddGear(amtTaken);
                        }
                        else
                        {
                            //taking all of it
                            actor.AddGear(this);
                            Level.RemoveObjectFromMap(this);
                        }
                        actor.Level.Host.DisplayMessage(actor.DisplayName + " takes the " + amtTaken.DisplayLabel + ".");
                        actor.Level.Host.ActorUpdated(actor);
                        break;
                    }
            }
        }

        //actions presented when the item is a piece of gear and is selected from the list
        public virtual List<string> GetGearActions(Actor actor)
        {
            List<string> lst = _actions;
            lst.Clear();

            //can always drop the item
            lst.Add("Drop");

            return lst;
        }
        public virtual void ProcessGearAction(Actor actor, string action, int qty)
        {
            if (actor == null)
                return;
            if (Owner != actor)
                return;

            switch (action)
            {
                case "Drop":
                    {
                        break;
                    }
            }
        }

        //private members
        private static List<string> _actions = new List<string>();
    }
}
